<?php
class Chat {
    private $serv = null;
    const USER_GUILD_ID     = 'WebSocketUserGuildId:';       // 玩家公会id
    const GUILD_MEMBER_LIST = 'WebSocketGuildMemberList:';   // 公会成员列表
    const USER_FD           = 'WebSocketUserFd';             // 玩家webSocket Fd
    const GUILD_CHAT        = 'GuildChat:';                  // 公会聊天内容
    const GUILD_PLAT        = 'WebSocketFromPlatform';      // 来源平台
    public function __construct(){
        $redis = $this->connectRedis();
        $redis->del($redis->keys('WebSocket*'));
        $this->closeRedis($redis);
        $fileDir = dirname(__FILE__);
        $this->serv = new swoole_websocket_server("0.0.0.0",8000,SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
        $this->serv->set(array(
            'task_worker_num' => 1,   // 单个task的处理耗时，如100ms，那一个进程1秒就可以处理1/0.1=10个task
            'ssl_cert_file' => $fileDir.'/shinezone.com.crt',
            'ssl_key_file' => $fileDir.'/shinezone.com.key',
            'heartbeat_check_interval' => 30,
            'heartbeat_idle_time' => 50,
            'dispatch_mode' => 2,
            'worker_num' => 16,
            'max_conn' => 65535,
            'max_request' => 10000
        ));
        $this->serv->on("open",array($this,"onOpen"));
        $this->serv->on("message",array($this,"onMessage"));
        $this->serv->on("Task",array($this,"onTask"));
        $this->serv->on("Finish",array($this,"onFinish"));
        $this->serv->on("close",array($this,"onClose"));
        $this->serv->start();
    }

    /**
     * @param $serv
     * @param $request
     */
    public function onOpen($serv, $request){
        // 校验客户端连接的合法性,无效的连接不允许连接
        $this->checkAccess($serv, $request);
    }

    /**
     * @param $serv
     * @param $frame
     */
    public function onMessage($serv , $frame){
        $data = json_decode($frame->data, true);
        if (!isset($data['type'])) {
            $this->serv->close($frame->fd);
        } else {
            if ($data['type'] == 1 && (!isset($data['guildId']) || !$data['guildId'])) {
                $this->serv->close($frame->fd);
            }  else {
                $data['fd'] = $frame->fd;
                $this->serv->task(json_encode($data));
            }
        }
    }

    /**
     * @param $serv
     * @param $task_id  任务id
     * @param $from_id  来自于哪个worker进程
     * @param $data     任务的内容
     * @return string
     */
    public function onTask($serv, $task_id, $from_id, $data){
        $data = json_decode($data,true);
        $fd = $data['fd'];
        $msg = array(
            'code' => 0,
            'data' => array()
        );
        /**
         * @var $redis Redis
         */
        switch ($data['type']) {
            case 0:
                // 心跳检测 不做处理
                return 'Finished';
            case 1:
                $redis = $this->connectRedis();
                // 登录后传递玩家信息
                $userInfo = array(
                    'avatarUrl' => isset($data['avatarUrl']) ? $data['avatarUrl'] : 0,
                    'nickName' => isset($data['nickName']) ? $data['nickName'] : 0,
                    'level' => isset($data['level']) ? $data['level'] : 0,
                    'guildId' => isset($data['guildId']) ? $data['guildId'] : 0,
                    'userId' => isset($data['userId']) ? $data['userId'] : 0,
                    'rank' => isset($data['rank']) ? $data['rank'] : 0,
                    'rankEndTime' => isset($data['rankEndTime']) ? $data['rankEndTime'] : 0,
		            'headFrame' => isset($data['headFrame']) ? $data['headFrame'] : 0,
		            'platform' => isset($data['platform']) ? $data['platform'] : 0,
                );
                // 保存用户数据
		        $redis->set(self::GUILD_PLAT.$fd,$data['platform']);
		        $redis->hMset($data['platform'].'-'.self::USER_GUILD_ID.$fd,$userInfo);
                $redis->hSet($data['platform'].'-'.self::GUILD_MEMBER_LIST.$data['guildId'],$fd,$fd);
                $msg['type'] = $data['type'];
                $msg['data'] = array();
                $this->sendMsg($data['platform'].'-'.$data['guildId'],json_encode($msg),$redis);
                $this->closeRedis($redis);
                break;
            case 2:
                // 发送消息
                if (!isset($data['message']) || !$data['message']) {
                    // 消息为空时 不做处理
                    return 'Finished';
                }
                $redis = $this->connectRedis();
		        $platform = $redis->get(self::GUILD_PLAT.$fd);
                $userInfo = $redis->hGetAll($platform.'-'.self::USER_GUILD_ID.$fd);
                if (!$userInfo || !isset($userInfo['guildId']) || !$userInfo['guildId']) {
                    // 用户数据为空时 不做处理
                    echo "type 2 userInfo is null:".json_encode($userInfo,true)."\n";
                    $this->closeRedis($redis);
                    return 'Finished';
                }
                $userInfo['message'] = $data['message'];
                $msg['type'] = $data['type'];
                $msg['data'] = $userInfo;
                $this->sendMsg($userInfo['platform'].'-'.$userInfo['guildId'],json_encode($msg),$redis);
                $redis->lTrim($userInfo['platform'].'-'.self::GUILD_CHAT.$userInfo['guildId'],0,28);
                $redis->lPush($userInfo['platform'].'-'.self::GUILD_CHAT.$userInfo['guildId'],json_encode($msg,true));
                $this->closeRedis($redis);
                break;
            case 3:
            case 5:
                $redis = $this->connectRedis();
                $platform = $redis->get(self::GUILD_PLAT.$fd);
                $userInfo = $redis->hGetAll($platform.'-'.self::USER_GUILD_ID.$fd);
                if (!$userInfo || !isset($userInfo['guildId']) || !$userInfo['guildId']) {
                    // 用户数据为空时 不做处理
                    echo "type 3 userInfo is null:".json_encode($userInfo,true)."\n";
                    $this->closeRedis($redis);
                    return 'Finished';
                }
                $msg['data']['message'] = $data['message'];
                $msg['type'] = $data['type'];
                $this->sendMsg($userInfo['platform'].'-'.$userInfo['guildId'],json_encode($msg),$redis);
                $this->closeRedis($redis);
                break;
            case 4:
                $redis = $this->connectRedis();
                $platform = $redis->get(self::GUILD_PLAT.$fd);
                $userInfo = $redis->hGetAll($platform.'-'.self::USER_GUILD_ID.$fd);
                if (!$userInfo || !isset($userInfo['guildId']) || !$userInfo['guildId']) {
                    // 用户数据为空时 不做处理
                    echo "type 4 userInfo is null:".json_encode($userInfo,true)."\n";
                    return 'Finished';
                }

                // 登录后传递玩家信息
                $userId = isset($data['userId']) ? $data['userId'] : 0;
                if ($userId == 0 || $data['level'] == 0){
                    $this->closeRedis($redis);
                    break;
                }
                $userInfo['level'] = $data['level'];
                $userInfo['headFrame'] = isset($data['headFrame']) ? $data['headFrame'] : 0;

                $redis->hMset($userInfo['platform'].'-'.self::USER_GUILD_ID.$fd,$userInfo);
                $redis->hSet($userInfo['platform'].'-'.self::GUILD_MEMBER_LIST.$userInfo['guildId'],$fd,$fd);
                $msg['type'] = $data['type'];
                $msg['data'] = $userInfo;
                $this->sendMsg($userInfo['platform'].'-'.$userInfo['guildId'],json_encode($msg),$redis);
                $this->closeRedis($redis);
                break;
        }
        return "Finished";
    }

    public function onClose($serv, $fd){
        $redis = $this->connectRedis();
        $platform = $redis->get(self::GUILD_PLAT.$fd);
        $userInfo = $redis->hGetAll($platform.'-'.self::USER_GUILD_ID.$fd);
        if ($userInfo) {
            $guildId = isset($userInfo['guildId']) ? $userInfo['guildId'] : 0;
            $this->delUserInfo($guildId.'-'.$userInfo['platform'],$fd,$redis);
        }

        $this->closeRedis($redis);
    }

    /**
     * @param $guildMsg
     * @param $pushMsg
     * @param $redis Redis
     */
    public function sendMsg($guildMsg,$pushMsg,$redis){
	$guildData = explode('-',$guildMsg);
	$guild = $guildData[1];
	$platform = $guildData[0];
        $memberList = $redis->hGetAll($platform.'-'.self::GUILD_MEMBER_LIST.$guild);
        if ($memberList) {
            foreach($memberList as $fd) {
                $fd = intval($fd);
                try {
                    if ($this->serv->connection_info($fd)) {
                        $this->serv->push($fd, $pushMsg);
                    } else {
                        $this->delUserInfo($guild.'-'.$platform,$fd,$redis);
                    }
                } catch (Exception $e) {
                    $this->delUserInfo($guild.'-'.$platform,$fd,$redis);
                }
            }
        }
    }

    /**
     * @param $serv
     * @param $task_id  任务id
     * @param $data     任务处理内容的结果
     */
    public function onFinish($serv, $task_id, $data){
    }

    /**
     * 校验客户端连接的合法性,无效的连接不允许连接
     * @param $serv
     * @param $request
     * @return bool
     */
    public function checkAccess($serv, $request) {
      $privateKey = 'a4FgAs1d7Sg8Ndfs6c2def5360dd26';
        if (!isset($request->get) || !isset($request->get['sign']) || !isset($request->get['reqTime'])) {
            $this->serv->close($request->fd);
            return false;
        }
        $sign = $request->get['sign'];
        $reqTime = $request->get['reqTime'];
        if ($sign != md5($privateKey.$reqTime)) {
            $this->serv->close($request->fd);
            return false;
        }
        return true;
    }

    /**
     * @param $guildMsg
     * @param $myFd
     * @param $redis Redis
     */
    public function delUserInfo($guildMsg,$myFd,$redis) {
	$guildData = explode('-',$guildMsg);
	$guild = $guildData[0];
	$platform = $guildData[1];
        $redis->hDel($platform.'-'.self::GUILD_MEMBER_LIST.$guild,$myFd);
        $redis->delete($platform.'-'.self::USER_GUILD_ID.$myFd);
    }

    /**
     * @return Redis
     */
    public function connectRedis() {
        $redisConfig = array(
            'server' => '127.0.0.1',
            'port' => 6379,
            'password' => null
        );
        $redis = new Redis();
        $redis->connect($redisConfig['server'],$redisConfig['port']);
        if ($redisConfig['password']) {
            $redis->auth($redisConfig['password']);
        }
        return $redis;
    }

    /**
     * @param $redis Redis
     */
    public function closeRedis($redis) {
        $redis->close();
    }
}

new Chat();
